package main

import (
	"bytes"
	"encoding/hex"
	"errors"
	"flag"
	"fmt"
	"net"
	"os"
	"strings"
)

func main() {
	var mac string
	var nic string
	flag.StringVar(&mac, "m", "", "specific device mac address")
	flag.StringVar(&nic, "n", "", "specific host network interface")
	flag.Parse()
	cmd, err := buildMagicPacket(mac)
	if err != nil {
		fmt.Println("build packet failed: " + err.Error())
		os.Exit(-1)
	}
	err = sendMagicPacket(cmd, nic)
	if err != nil {
		fmt.Printf(err.Error())
		os.Exit(-1)
	}
	os.Exit(0)
}

func buildMagicPacket(mac string) ([]byte, error) {
	// 检查mac合法性
	if mac == "" {
		return nil, errors.New("MAC必须指定")
	}
	hw := strings.Replace(strings.Replace(mac, ":", "", -1), "-", "", -1)
	if len(hw) != 12 {
		return nil, errors.New("MAC长度不合法: " + mac)
	}
	macHex, err := hex.DecodeString(hw)
	if err != nil {
		return nil, errors.New("MAC解析失败: " + mac)
	}
	var buff bytes.Buffer
	buff.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF})
	for i := 0; i < 16; i++ {
		buff.Write(macHex)
	}
	// 获得唤醒魔包
	mp := buff.Bytes()
	if len(mp) != 102 {
		return nil, errors.New("唤醒魔包长度不合法: " + buff.String())
	}
	return mp, nil
}

func sendMagicPacket(mp []byte, nic string) error {
	if nic == "" {
		return errors.New("nic必须指定")
	}
	sender := net.UDPAddr{}
	if len(nic) != 0 {
		ip, err := interfaceIPv4ByName(nic)
		if err != nil {
			return errors.New("查找网络接口失败")
		}
		sender.IP = ip
	}
	target := net.UDPAddr{
		IP: net.IPv4bcast,
	}
	conn, err := net.DialUDP("udp", &sender, &target)
	if err != nil {
		return errors.New("创建UDP失败")
	}
	defer func() {
		_ = conn.Close()
	}()
	_, err = conn.Write(mp)
	return err
}

func interfaceIPv4ByName(nic string) (net.IP, error) {
	inter, err := net.InterfaceByName(nic)
	if err != nil {
		return nil, err
	}
	// 检查网卡是否正在工作
	if (inter.Flags & net.FlagUp) == 0 {
		return nil, errors.New("网卡未启用")
	}
	addrs, err := inter.Addrs()
	if err != nil {
		return nil, err
	}
	for _, addr := range addrs {
		if ip, ok := addr.(*net.IPNet); ok {
			if ipv4 := ip.IP.To4(); ipv4 != nil {
				return ipv4, nil
			}
		}
	}
	return nil, errors.New("找不到网卡绑定的IP")
}
